lwipfandomcom-20200215-history
Writing a device driver
Required settings Even if you are not creating an Ethernet driver, the netif/ethernetif.c file offers a good outline for what a driver should include. The following is a list of things that must be defined. Functions Initialization * err_t myif_init(struct netif *netif) - This is the initialization function that should be passed to netif_add (see Network interfaces management in the Application Developers Manual for an example), where it will be run. Output * err_t myif_link_output(struct netif *netif, struct pbuf *p) - Called when a raw link packet is ready to be transmitted. This function should not add any more headers. You must set netif->linkoutput to the address of this function. * err_t myif_output(struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr) - Called by ip_output when a packet is ready for transmission. Any link headers will be added here. This function should call the myif_link_output function when the packet is ready. You must set netif->output to the address of this function. If your driver supports ARP, you can simply set netif->output to etharp_output. Input *When you have received a packet (do not remove link headers!), you should pass the packet to my_netif->input, which should have been set by netif_add to the appropriate function (see Network interfaces management). *'Do not pass received packets to one of the input functions directly!' Whether this be ethernet_input or ip_input: things might work like this, but you lose portability and might be incompatible to future changes and risk threading problems! (Plus you risk annoying people on the mailing list with the problems you get by doing this - there will be problems, trust me :-) *This function must be set to the correct input function by your initialization code (via netif_add): **With NO_SYS=0, this must be set to tcpip_input for all netif types (whether ethernet, PPP, slipif, etc.) **With NO_SYS=1, ***For ethernet netifs, this must be set to ethernet_input (pass the pbuf including link headers) ***For non-ethernet netifs, this must be set to ip_input (pass the pbuf without link headers, p->payload pointing to the IP header) struct netif settings The following structure variables should be set during initialization: State * my_netif->state - Point this to any internal structure that you would like to keep track of for the "state" of your interface. This could include status information, statistics, or anything else. * my_netif->hwaddr_len - The number of bytes in the link address (e.g., MAC address for Ethernet) * my_netif->hwaddr[] - The hardware address itself. * my_netif->mtu - The MTU (maximum transmission unit) for the interface. This defines the maximum sized packet that may be sent on the link, including headers. If an application attempts to send a packet larger than this, lwIP will split the packet up into pieces of this size. * my_netif->name2 - Two-character name, like "en" for Ethernet. This can be used to get a netif by name, via netif_find * my_netif->num - An optional number to indicate the number in this "series", if there are multiple netif's of the same type. For example, perhaps there are two interfaces named "en"; they can be differentiated by this number. This is completely optional and largely unused by lwIP. Functions * my_netif->output - Set this equal to the myif_output described above. * my_netif->link_output - Set this equal to the myif_link_output described above. * my_netif->input - Do not set this. This is set by netif_add. Flags * my_netif->flags - Should set any of the following flags that apply to this interface. ** NETIF_FLAG_BROADCAST - If this interface can transmit broadcast packets. ** NETIF_FLAG_POINTTOPOINT - If this interface is on an end of a PPP connection. ** NETIF_FLAG_ETHARP - If this interface uses ARP (see the section below for more information). ** NETIF_FLAG_LINK_UP - Set this flag when the link is established (see note below for more capability). Do not set these flags directly, since other flags are set and reset elsewhere in lwIP (like NETIF_FLAG_UP). Set via an OR, for example my_netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP; Optional variables The following variables are disabled by default; if you would like to use these, then make sure to set the right option inside of lwipopts.h. * my_netif->hostname (option LWIP_NETIF_HOSTNAME) - The hostname assigned to this interface. * Various SNMP settings (option LWIP_SNMP) Link change events A new feature in version 1.3.0 is the ability to initiate link-change events by the driver. If you set the LWIP_NETIF_LINK_CALLBACK in lwipopts.h, then your driver should call the netif_set_link_up and netif_set_link_down functions instead of setting the NETIF_FLAG_LINK_UP flag directly. (If you are using the tcpip thread, then you should call tcpip_callback with an argument of one of these two functions; this will protect against concurrent access, specifically in the ARP modules.) A link change will trigger certain events in the code. Currently, when link goes up, the gratuitous ARP is generated. Incorporating ARP for your driver If the interface supports ARP, then you should set the NETIF_FLAG_ETHARP flag in my_netif->flags. There are a couple options for how to pass packets into the stack, depending on whether you use the optional tcpip thread or not. Unthreaded Output. When an IP packet is ready for transmission, it calls netif->output. Most likely, you can set this to directly call the following ARP output function for an interface, which subsequently calls netif->linkoutput for its frames. * err_t etharp_output() (Note that function argument order has changed in version 1.3.0; prior to this version, it was necessary to have a wrapper function to swap the arguments.) Input. Any incoming packet (whether it is an ARP packet or an IP packet) should be passed to the netif->input function: *my_netif->input() This is the function which has been set by your initialization code via netif_add. Depending on the type of the netif: **Ethernet netif: ***This should have been set to ethernet_input(). ***The full packet (including ARP header) must be passed. **Non-ethernet netif (e.g. PPP or slipif): ***This should have been set to ip_input() . ***Link headers must be removed (pbuf->payload must point to the IP header) Timer. Finally, the following timer must be invoked every ARP_TMR_INTERVAL milliseconds (typically 5000, or 5 seconds): * void etharp_tmr() With the tcpip thread Output. For output, we use the same function as above, since we assume only the tcpip thread will call this function. * err_t etharp_output() (Note that function argument order has changed in version 1.3.0; prior to this version, it was necessary to have a wrapper function to swap the arguments.) Input. For input, we must pass the packet into the tcpip thread for processing in order to protect the changes to the ARP table. You need to define ETHARP_TCPIP_ETHINPUT and use the following function: * my_netif->input() (which should have been set to tcpip_input() by your application via netif_add) - for all incoming packets Timer. The timer is already handled by the tcpip thread. Gratuitous ARP A "gratuitous ARP" can be generated by a call etharp_query(our_netif, its_ip_addr, NULL) (see RFC 3220, Section 4.6). Starting in version 1.3.0, the gratuitous ARP is generated by netif_set_up() and should not be done in the driver or application code. Notes on Zero-Copy Network interface drivers * myif_link_output: after LwIP has called this function it frees the pbuf (as soon as it pleases). This would mean that the network driver may not access the buffer after link_output had returned. So link_output would either need to complete the transmit before returning, or it would need to copy the data somewhere else. However it is possible to call pbuf_ref(p); inside link_output and call pbuf_free(p); later, as soon as the buffer is not needed any more (=the package transmit has completed). Yet the network driver may not modify the pbuf, as it may still be used by LwIP, for example for TCP retransmits. The network driver may also not assume that the pbuf memory is actually freed when it calls pbuf_free. In case LwIP still uses the buffer, only the reference counter is decremented. Implementing a network driver as described above assumes that LwIP may free the pbuf, but it does not modify it after link_output has been called. By looking at the LwIP source code we see that is mostly true for tcp/ip (analysis incomplete by now): ** netif/etharp.c has calls to netif->linkoutput() in the following functions: *** etharp_arp_input calls pbuf_free at end of function => ok *** etharp_raw calls pbuf_free at end of function => ok *** etharp_send_ip **** called by update_arp_entry, which calls pbuf_free => ok **** etharp_output: usually called via netif->output() **** etharp_query is called by etharp_output, see above. ** netif/ethernetif.c ? ** netif/ppp/ppp_oe.c ? ** the following files have calls to netif->output() *** core/ipv4/ip.c ? *** core/ipv4/igmp.c ? *** core/ipv4/ip_frag.c ? *** core/ipv6/ip6.c ? *** netif/loopif.c ? *** netif/slipif.c ? *** netif/ethernetif.c ? *** netif/ppp/ppp.c ? ** ... ** tcp: calls to netif->output() via ip_output();/ip_output_if(); ** tcp_out.c: *** tcp_output calls pbuf_free after calling ip_output/ipoutput_if => ok *** tcp_rst calls pbuf_free after calling ip_output/ipoutput_if => ok *** tcp_keepalive calls pbuf_free after calling ip_output/ipoutput_if => ok *** tcp_zero_window_probe calls pbuf_free after calling ip_output/ipoutput_if => ok *** tcp_output_segment does not call pbuf_free itself, it is called by tcp_output. The segments from tcp_output_segment are moved to pcb->unacked. In tcp_in.c they are freed in tcp_receive, l.855. after they have been acknowledged. In case they are resent, they are moved to pcb->unsent in tcp_rexmit_rto(), then sent via tcp_output() and tcp_output_segment(). In tcp_output_segment the tcp header is (re-)calculated and thus the buffer is modified. > So it currently seems like the tx buffer is only modified during tcp retransmit. That means that the network driver needs to complete its transmit before a retransmit can be initiated by LwIP, which should be feasible in most cases. However, take a look at that mail from the mailing list archive. category:LwIP Platform Developers Manual